package com.retail.lottery.colalottery.service.impl;

import com.retail.lottery.colalottery.enums.ErrorCodeEnum;
import com.retail.lottery.colalottery.enums.LotteryStatusEnum;
import com.retail.lottery.colalottery.exception.BusinessException;
import com.retail.lottery.colalottery.service.CommonService;
import com.retail.lottery.colalottery.utils.DateUtil;
import com.retail.lottery.colalottery.vo.request.LotteryTimeRequest;
import com.retail.lottery.colalottery.vo.response.LotteryTimeResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CommonServiceImpl implements CommonService {

    @Value("${lottery-threshold}")
    private String lotteryThreshold;

    @Override
    public LotteryTimeResponse lotteryTimeQuery(LotteryTimeRequest request) {
        LotteryTimeResponse lotteryTimeResponse = new LotteryTimeResponse();
        lotteryTimeResponse.setCustomerId(request.getCustomerId());
        if(CollectionUtils.isEmpty(request.getReducePointChangeDetailList())){
            lotteryTimeResponse.setLotteryInfoList(Lists.newArrayList());
            return lotteryTimeResponse;
        }

        List<String> lotteryDateList = request.getLotteryDt().stream().sorted().collect(Collectors.toList());


        List<LotteryTimeRequest.ReducePointChangeDetail> reducePointChangeDetailList = request.getReducePointChangeDetailList();
        lotteryTimeResponse.setLotteryInfoList(findThresholdCrossingDetails(reducePointChangeDetailList,
                Arrays.stream(lotteryThreshold.split(",")).map(x -> {
                    return Integer.valueOf(x);
                }).collect(Collectors.toList()), lotteryDateList));
        return lotteryTimeResponse;
    }


    /**
     * 根据积分变化明细查找达到阈值的抽奖信息
     * 
     * 该方法的主要功能是：
     * 1. 根据用户积分变化明细计算累计积分
     * 2. 当累计积分达到预设阈值时，生成对应的抽奖信息
     * 3. 抽奖信息包括：抽奖状态、序号、获得积分时间、抽奖日期等
     * 
     * 算法逻辑：
     * 1. 将积分明细按创建时间升序排序
     * 2. 遍历积分明细，累加积分值
     * 3. 当累计积分达到或超过某个阈值时，生成对应序号的抽奖信息
     * 4. 如果有对应的抽奖日期，则设置为可抽奖状态，否则为待抽奖状态
     * 5. 当超出最大阈值(48)后，每增加10积分赠送一次抽奖次数
     * 
     * @param pointDetails 积分变化明细列表，包含每次积分变化的时间和积分值
     * @param sortedThresholds 预设的积分阈值列表，按升序排列
     * @param lotteryDateList 可抽奖日期列表，与阈值一一对应
     * @return 抽奖信息列表，包含达到阈值时生成的抽奖信息
     */
    public List<LotteryTimeResponse.LotteryInfo> findThresholdCrossingDetails(
            List<LotteryTimeRequest.ReducePointChangeDetail> pointDetails,
            List<Integer> sortedThresholds,
            List<String> lotteryDateList) {

        // 如果积分明细为空，直接返回空列表
        if (pointDetails == null) {
            return Collections.emptyList();
        }

        // 1. 按创建时间升序排序，确保按时间顺序处理积分变化
        List<LotteryTimeRequest.ReducePointChangeDetail> sortedDetails = pointDetails.stream()
                .sorted(Comparator.comparing(LotteryTimeRequest.ReducePointChangeDetail::getCreateTime))
                .collect(Collectors.toList());

        // 2. 准备结果集合和阈值索引
        // result: 存储生成的抽奖信息
        // accumulatedPoints: 累计积分值
        // thresholdIndex: 当前处理的阈值索引
        List<LotteryTimeResponse.LotteryInfo> result = new ArrayList<>();
        int accumulatedPoints = 0;
        int thresholdIndex = 0;
        
        // 获取最大阈值，用于处理超出后的额外抽奖逻辑
        int maxThreshold = sortedThresholds.get(sortedThresholds.size() - 1);
        // 额外抽奖计数器（超出最大阈值后每10积分一次）
        int extraLotteryCount = 0;

        // 3. 遍历积分明细，计算累计积分并检查是否达到阈值
        for (LotteryTimeRequest.ReducePointChangeDetail detail : sortedDetails) {
            // 累加当前积分变化值
            accumulatedPoints += detail.getPoints();
            
            // 4. 检查累计积分是否达到当前或后续的阈值
            // 使用while循环处理可能一次性跨越多个阈值的情况
            while (thresholdIndex < sortedThresholds.size() &&
                    accumulatedPoints >= sortedThresholds.get(thresholdIndex)) {
                // 创建新的抽奖信息对象
                LotteryTimeResponse.LotteryInfo info = new LotteryTimeResponse.LotteryInfo();
                
                // 默认设置为待抽奖状态
                info.setLotteryStatus(LotteryStatusEnum.WAIT_LOTTERY.getCode());
                
                // 设置抽奖序号（从1开始）
                info.setIndex(thresholdIndex + 1);
                
                // 设置获得积分的时间（即达到阈值的时间）
                info.setPointGetDt(detail.getCreateTime());
                
                // 如果当前阈值序号在抽奖日期列表范围内，则设置对应抽奖日期并更新状态为可抽奖
                if(thresholdIndex+1<=lotteryDateList.size()){
                    info.setLotteryDt(lotteryDateList.get(thresholdIndex));
                    info.setLotteryStatus(LotteryStatusEnum.LOTTERY.getCode());
                }
                
                // 将生成的抽奖信息添加到结果列表中
                result.add(info);
                
                // 移动到下一个阈值
                thresholdIndex++;
            }

            // 5. 处理超出最大阈值后的额外抽奖逻辑
            // 当累计积分超过最大阈值时，每增加10积分赠送一次抽奖机会
            if (accumulatedPoints > maxThreshold) {
                // 计算超出部分可以兑换的额外抽奖次数
                int newExtraLotteryCount = (accumulatedPoints - maxThreshold) / 10;
                
                // 如果额外抽奖次数增加了，则添加相应的抽奖信息
                while (extraLotteryCount < newExtraLotteryCount) {
                    // 创建新的抽奖信息对象
                    LotteryTimeResponse.LotteryInfo info = new LotteryTimeResponse.LotteryInfo();
                    
                    // 默认设置为待抽奖状态
                    info.setLotteryStatus(LotteryStatusEnum.WAIT_LOTTERY.getCode());
                    
                    // 设置抽奖序号（基础阈值数量 + 额外抽奖序号）
                    info.setIndex(sortedThresholds.size() + extraLotteryCount + 1);
                    
                    // 设置获得积分的时间
                    info.setPointGetDt(detail.getCreateTime());
                    
                    // 如果额外抽奖序号在抽奖日期列表范围内，则设置对应抽奖日期并更新状态为可抽奖
                    // 注意：额外抽奖的索引是基础阈值数量 + 额外抽奖序号 - 1
                    // 因为索引从0开始，但序号从1开始
                    int lotteryDateIndex = sortedThresholds.size() + extraLotteryCount;
                    if(lotteryDateIndex < lotteryDateList.size()){
                        info.setLotteryDt(lotteryDateList.get(lotteryDateIndex));
                        info.setLotteryStatus(LotteryStatusEnum.LOTTERY.getCode());
                    }
                    
                    // 将生成的额外抽奖信息添加到结果列表中
                    result.add(info);
                    
                    // 增加额外抽奖计数器
                    extraLotteryCount++;
                }
            }

            // 如果所有阈值都已处理完毕，则提前结束循环 -- 本期调整为无限抽奖上限
//            if (thresholdIndex >= sortedThresholds.size()) {
//                break;
//            }
        }
        
        // 返回生成的抽奖信息列表
        return result;
    }
}
